#include "StdAfx.h"
#include "SkyModel.h"
#include "Level.h"
#include "GraphCamera.h"
#include "PhysX.h"

#include <iostream>
#include <fstream>
#include <vector>

#include "SkyBox.h"
#include "Skydome.h"
#include "Clouds.h"

using namespace std;

SkyModel::SkyModel(Level *pLevel, InitInfo& info)
	:LevelElement()
	,m_InitInfo(info)
	,m_pSkydome(0)
	,m_pClouds(0)
	,DegToRad((float)(D3DX_PI / 180.0f))
{
	m_pLevel = pLevel;
}

SkyModel::~SkyModel(void)
{
	if(m_pSkydome != 0)
		delete m_pSkydome;
}
void SkyModel::Initialize(ContentManager *pContentManager)
{
	//m_pClouds = new Clouds(m_pLevel);
	//m_pClouds->Initialize(pContentManager);

	if(m_InitInfo.Type == SkyModel::Skytype::SkyDome)
	{
		Skydome::InitInfo skydomeinit;
		skydomeinit.Texture = _T("./Textures/SkyBox/skydome2.dds");
		skydomeinit.DomeN = 32;
		skydomeinit.radiusHoriz = 2000.0;
		skydomeinit.radiusUp = 2000.0f;
		skydomeinit.Position = m_InitInfo.CenterPosition;

		m_pSkydome = new Skydome(m_pLevel, skydomeinit);
		m_pSkydome->Initialize(pContentManager);
	}
	

	Skydome::ScatteringInfo &s = m_pSkydome->GetScatterInformation();

	s.SunPos = 0.0f;

    s.SunDirection = D3DXVECTOR3((float)cosf(90 * DegToRad),
                                (float)cosf(s.SunPos * DegToRad),
                                (float)sinf(s.SunPos * DegToRad));

	s.HGgFunction = (0.79999998f);
    s.InscatteringMultiplier = 1.0f;//0.909999967f;
    s.BetaRayMultiplier = (10.0f);
    s.BetaMieMultiplier = (0.00060000003f);
    s.SunIntensity = (0.3f);
    s.Turbitity = (0.8f);

    calculateScatteringConstants();
}
void SkyModel::Tick(const InputState & refInputState)
{
	Skydome::ScatteringInfo &s = m_pSkydome->GetScatterInformation();
	m_pSkydome->Tick(refInputState);
	s.SunPos += 0.01f;
	if(s.SunPos > 270)
		s.SunPos -= 360;

}
void SkyModel::Draw(const RenderContext* pRenderContext)
{
	Skydome::ScatteringInfo &s = m_pSkydome->GetScatterInformation();

	s.SunDirection.y = (float)cosf(s.SunPos * DegToRad);
	s.SunDirection.z = (float)sinf(s.SunPos * DegToRad);

	D3DXVECTOR3 vecZenith = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
	float thetaS = D3DXVec3Dot(&s.SunDirection, &vecZenith);
	thetaS = (float)acosf(thetaS);

	computeAttenuation(thetaS);

	m_pSkydome->Draw(pRenderContext);
	//m_pClouds->Draw(pRenderContext, s);

}
void SkyModel::DrawRefraction(const RenderContext* pRenderContext, D3DXPLANE refractionplane)
{
	m_pSkydome->DrawRefraction(pRenderContext, refractionplane);
}
void SkyModel::DrawReflection(const RenderContext* pRenderContext, D3DXMATRIX reflectionviewmatrix, D3DXPLANE reflectionplane)
{
	m_pSkydome->DrawReflection(pRenderContext, reflectionviewmatrix, reflectionplane);
}
void SkyModel::calculateScatteringConstants()
{
	Skydome::ScatteringInfo &s = m_pSkydome->GetScatterInformation();

    const float n = 1.0003f;
    const float N = 2.545e25f;
    const float pn = 0.035f;

	float fLambda[3];
    float fLambda2[3];
    float fLambda4[3];

    fLambda[0]  = 1.0f / 650e-9f;   // red
    fLambda[1]  = 1.0f / 570e-9f;   // green
    fLambda[2]  = 1.0f / 475e-9f;   // blue

    for( int i = 0; i < 3; ++i )
    {
        fLambda2[i] = fLambda[i]*fLambda[i];
        fLambda4[i] = fLambda2[i]*fLambda2[i];
    }

    D3DXVECTOR3 vLambda2 = D3DXVECTOR3( fLambda2[0], fLambda2[1], fLambda2[2] ); 
    D3DXVECTOR3 vLambda4 = D3DXVECTOR3( fLambda4[0], fLambda4[1], fLambda4[2] ); 

    // Rayleigh scattering constants

    float fTemp = (float)D3DX_PI * (float)D3DX_PI * (n * n - 1.0f) * (n * n - 1.0f) * (6.0f + 3.0f * pn) / (6.0f - 7.0f * pn) / N;
    float fBeta = 8.0f * fTemp * (float)D3DX_PI / 3.0f;
         
    s.BetaRay = fBeta * vLambda4;

    float fBetaDash = fTemp / 2.0f;

    s.BetaDashRay = fBetaDash * vLambda4;

    // Mie scattering constants

    float T       = 2.0f;
    float c       = (6.544f * T - 6.51f) * 1e-17f;
    float fTemp2  = 0.434f * c * (2.0f * (float)D3DX_PI) * (2.0f* (float)D3DX_PI) * 0.5f;

    s.BetaDashMie = fTemp2 * vLambda2;

    float K[3]    = {0.685f, 0.679f, 0.670f};
    float fTemp3  = 0.434f * c * (float)D3DX_PI * (2.0f * (float)D3DX_PI) * (2.0f * (float)D3DX_PI);

    D3DXVECTOR3 vBetaMieTemp = D3DXVECTOR3(K[0] * fLambda2[0], K[1] * fLambda2[1], K[2] * fLambda2[2]);

    s.BetaMie = fTemp3 * vBetaMieTemp;
    s.DiffuseConstant = D3DXVECTOR3(0.138f, 0.113f, 0.08f);

}
void SkyModel::computeAttenuation(float a_theta)
{
	Skydome::ScatteringInfo &s = m_pSkydome->GetScatterInformation();

    float fBeta = 0.04608365822050f * s.Turbitity - 0.04586025928522f;
    float fTauR, fTauA;
    float fTau[3];
    //float m = 1.0f / ((float)cosf( a_theta ) + 0.15f
	//	* (float)powf( 93.885f - a_theta / (float)D3DX_PI * 180.0f, -1.253f ));  // Relative Optical Mass
    
	float coseno = (float)cosf((double)a_theta);
    double factor = (double)a_theta / D3DX_PI * 180.0;
    double jarl = powf(93.885 - factor, -1.253);
    float potencia = (float)jarl;
    float m = 1.0f / (coseno + 0.15f * potencia);
	
	float fLambda[3] = {0.65f, 0.57f, 0.475f }; 

    for( int i = 0; i < 3; ++i )
    {
        // Rayleigh Scattering
        // lambda in um.
		float potencion = (float)powf((double)fLambda[i], -5.5f);
        fTauR = (float)exp( -m * 0.008735f * potencion);//(float)powf( fLambda[i], (-4.08f) ) );

        // Aerosal (water + dust) attenuation
        // beta - amount of aerosols present 
        // alpha - ratio of small to large particle sizes. (0:4,usually 1.3)

        const float fAlpha = 1.3f;
		potencion = (float)powf((double)fLambda[i], (double)-fAlpha);
          // lambda should be in um

		//if (m > 45.0f)
		//{
		//	fTau[i] = 0.0f;
		//} else {
			fTauA = (float)exp(-m * fBeta * potencion);
			fTau[i] = fTauR * fTauA;
		//}

    }
	if(s.SunPos > 90 && s.SunPos < 360)
		s.SunColorAndIntensity = D3DXVECTOR4( 0, 0, 0, 0);
	else
		s.SunColorAndIntensity = D3DXVECTOR4( fTau[0], fTau[1], fTau[2], s.SunIntensity * 100.0f );
}
float SkyModel::GetSunPos()
{
	return m_pSkydome->GetScatterInformation().SunPos;
}
D3DXVECTOR3 SkyModel::GetSunDirection()
{
	Skydome::ScatteringInfo &s = m_pSkydome->GetScatterInformation();

	return D3DXVECTOR3((float)cosf(90 * DegToRad),
                                (float)cosf(s.SunPos * DegToRad),
                                (float)sinf(s.SunPos * DegToRad));
}
D3DXCOLOR SkyModel::GetSunColor()
{
	Skydome::ScatteringInfo &s = m_pSkydome->GetScatterInformation();

	D3DXCOLOR color;
	color.r = s.SunColorAndIntensity.x;
	color.g = s.SunColorAndIntensity.y;
	color.b = s.SunColorAndIntensity.z;

	return color;
}